package com.polaris.live.widget.drag

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.AbsSeekBar
import android.widget.FrameLayout
import android.widget.HorizontalScrollView
import androidx.core.view.ViewCompat
import androidx.customview.widget.ViewDragHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.widget.ViewPager2
import com.lxj.xpopup.enums.LayoutStatus
import com.lxj.xpopup.enums.PopupPosition
import com.lxj.xpopup.util.XPopupUtils
import com.lxj.xpopup.widget.PopupDrawerLayout

/**
 * DrawerDragLayout
 *
 * @author Created by lucas on 2023/11/11 15:20
 */
open class DrawerDragLayout(context: Context, attributeSet: AttributeSet?) : FrameLayout(context, attributeSet) {

    private lateinit var mChild: View
    var status: LayoutStatus? = null

    private var listener: PopupDrawerLayout.OnCloseListener? = null
    private var onViewDestroyListener: OnViewDestroyListener? = null

    override fun onFinishInflate() {
        super.onFinishInflate()

        mChild = getChildAt(0)
    }

    private val callback: ViewDragHelper.Callback = object : ViewDragHelper.Callback() {
        override fun tryCaptureView(view: View, i: Int): Boolean {
            return enableDrag && !dragHelper.continueSettling(true) && status != LayoutStatus.Close
        }

        override fun getViewHorizontalDragRange(child: View): Int {
            return 1
        }

        override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
            return fixLeft(left)
        }

        override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
            calcFraction(left, dx)
        }

        private fun calcFraction(left: Int, dx: Int) {
            val listener = listener
            // fraction = (now - start) * 1f / (end - start)
            if (position == PopupPosition.Left) {
                fraction = (left + mChild.measuredWidth) * 1f / mChild.measuredWidth
                if (left == -mChild.measuredWidth && listener != null && status != LayoutStatus.Close) {
                    status = LayoutStatus.Close
                    listener.onClose()
                }
            } else if (position == PopupPosition.Right) {
                fraction = (measuredWidth - left) * 1f / mChild.measuredWidth
                if (left == measuredWidth && listener != null && status != LayoutStatus.Close) {
                    status = LayoutStatus.Close
                    listener.onClose()
                }
            }
            if (listener != null) {
                listener.onDrag(left, fraction, dx < 0)
                if (fraction == 1f && status != LayoutStatus.Open) {
                    status = LayoutStatus.Open
                    listener.onOpen()
                }
            }
        }

        override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
            super.onViewReleased(releasedChild, xvel, yvel)
            if (releasedChild === mChild && isToLeft && !canChildScrollLeft && xvel < -500) {
                close()
                return
            }
            var centerLeft = 0
            var finalLeft = 0
            if (position == PopupPosition.Left) {
                if (xvel < -1000) {
                    finalLeft = -mChild.measuredWidth
                } else {
                    centerLeft = -mChild.measuredWidth / 2
                    finalLeft = if (mChild.left < centerLeft) -mChild.measuredWidth else 0
                }
            } else {
                if (xvel > 1000) {
                    finalLeft = measuredWidth
                } else {
                    centerLeft = measuredWidth - mChild.measuredWidth / 2
                    finalLeft =
                        if (releasedChild.left < centerLeft) measuredWidth - mChild.measuredWidth else measuredWidth
                }
            }
            dragHelper.smoothSlideViewTo(mChild, finalLeft, releasedChild.top)
            ViewCompat.postInvalidateOnAnimation(this@DrawerDragLayout)
        }
    }

    private val dragHelper = ViewDragHelper.create(this, callback)

    var position = PopupPosition.Right
    private var fraction = 0f
    var enableDrag = true

    private var isIntercept = false
    private var x = 0f
    private var y = 0f
    private var downX = 0f
    private var downY = 0f
    private var isToLeft = false
    private var canChildScrollLeft = false

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        if (!enableDrag) return super.onInterceptTouchEvent(ev)
        if (dragHelper.continueSettling(true) || status == LayoutStatus.Close) return true
        isToLeft = ev.x < x
        x = ev.x
        y = ev.y
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = ev.x
                downY = ev.y
            }
            MotionEvent.ACTION_MOVE -> {
                val dx: Float = Math.abs(x - downX)
                val dy: Float = Math.abs(y - downY)
                if (dy > dx) {
                    //垂直方向滑动
                    return false
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                x = 0f
                y = 0f
            }
        }
        canChildScrollLeft = canScroll(this, ev.x, ev.y, 1)

        isIntercept = dragHelper.shouldInterceptTouchEvent(ev)
        if (isToLeft && !canChildScrollLeft) {
            return isIntercept
        }
        val canChildScrollHorizontal: Boolean = canScroll(this, ev.x, ev.y)
        return if (!canChildScrollHorizontal) isIntercept else super.onInterceptTouchEvent(ev)
    }

    private fun canScroll(group: ViewGroup, x: Float, y: Float, direction: Int): Boolean {
        for (i in 0 until group.childCount) {
            val child = group.getChildAt(i)
            val location = IntArray(2)
            child.getLocationInWindow(location)
            val rect = Rect(
                location[0], location[1], location[0] + child.width,
                location[1] + child.height
            )
            val inRect = XPopupUtils.isInRect(x, y, rect)
            if (inRect) {
                if (child is ViewGroup) {
                    return if (child is ViewPager) {
                        val pager = child
                        if (direction == 0) {
                            val b = pager.canScrollHorizontally(-1) || pager.canScrollHorizontally(1)
                            return pager.canScrollHorizontally(-1) || pager.canScrollHorizontally(1)
                        }
                        pager.canScrollHorizontally(direction)
                    } else if (child is HorizontalScrollView) {
                        val hsv = child
                        if (direction == 0) {
                            hsv.canScrollHorizontally(-1) || hsv.canScrollHorizontally(1)
                        } else hsv.canScrollHorizontally(direction)
                    } else if (child is ViewPager2) {
                        val rv = child.getChildAt(0) as RecyclerView
                        rv.canScrollHorizontally(-1) || rv.canScrollHorizontally(1)
                    } else {
                        canScroll(child, x, y, direction)
                    }
                } else {
                    if (child is AbsSeekBar && child.isEnabled()) {
                        return true
                    }
                }
            }
        }
        return false
    }

    private fun canScroll(group: ViewGroup, x: Float, y: Float): Boolean {
        return canScroll(group, x, y, 0)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (!enableDrag) return super.onTouchEvent(event)
        if (dragHelper.continueSettling(true)) return true
        if (event != null) {
            dragHelper.processTouchEvent(event)
        }
        return true
    }

    private fun fixLeft(left: Int): Int {
        var left = left
        if (position == PopupPosition.Left) {
            if (left < -mChild.measuredWidth) left = -mChild.measuredWidth
            if (left > 0) left = 0
        } else if (position == PopupPosition.Right) {
            if (left < measuredWidth - mChild.measuredWidth) left = measuredWidth - mChild.measuredWidth
            if (left > measuredWidth) left = measuredWidth
        }
        return left
    }

    override fun computeScroll() {
        super.computeScroll()
        if (dragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this)
        } else {
            if (status == LayoutStatus.Close) {
                onViewDestroyListener?.onViewDestroy(this)
            }
        }
    }

    /**
     * 关闭Drawer
     */
    fun close() {
        post {
            dragHelper.abort()
            if (context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
                dragHelper.smoothSlideViewTo(
                    mChild,
                    if (position == PopupPosition.Left) -mChild.measuredWidth else -measuredWidth,
                    0
                )
            } else {
                dragHelper.smoothSlideViewTo(
                    mChild,
                    if (position == PopupPosition.Left) -mChild.measuredWidth else measuredWidth,
                    0
                )
            }
            ViewCompat.postInvalidateOnAnimation(this@DrawerDragLayout)
        }
    }

    open fun setOnCloseListener(l: PopupDrawerLayout.OnCloseListener) {
        this.listener = l
    }

    open fun setOnViewDestroyListener(l: OnViewDestroyListener) {
        this.onViewDestroyListener = l
    }
}

fun interface OnViewDestroyListener {

    fun onViewDestroy(view: View)

}